import fs from 'node:fs';
import path from 'node:path';
import archiver from 'archiver';
import OSS from 'ali-oss';
import chalk from 'chalk';

chalk.level = 1;

/**
 * 递归列出目录中的所有文件
 * @param {string} dir - 要列出文件的目录
 * @param {string} baseDir - 基准目录，用于计算相对路径
 * @returns {Array} 文件路径数组
 */
function listFiles(dir, baseDir = dir) {
  let files = [];
  const items = fs.readdirSync(dir);
  
  for (const item of items) {
    const itemPath = path.join(dir, item);
    const stat = fs.statSync(itemPath);
    
    if (stat.isDirectory()) {
      files = files.concat(listFiles(itemPath, baseDir));
    } else {
      files.push(path.relative(baseDir, itemPath));
    }
  }
  
  return files;
}

/**
 * 将目录压缩为 zip 文件
 * @param {string} sourceDir - 要压缩的源目录
 * @param {string} outputDir - zip 文件输出目录
 * @param {string} fileName - 生成的 zip 文件名
 * @returns {Promise<void>}
 */
async function compressDist(sourceDir, outputDir, fileName) {
  // 确保源目录存在
  if (!fs.existsSync(sourceDir)) {
    throw new Error(`Source directory "${sourceDir}" does not exist`);
  }

  // 确保输出目录存在
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // 生成 zip 文件的完整路径
  const outputPath = path.join(outputDir, fileName);
  
  // 列出要压缩的文件
  console.log(chalk.blue(`[vite-plugin-dist-zip] Listing files in "${sourceDir}":`));
  const filesToCompress = listFiles(sourceDir);
  console.log(chalk.blue(`[vite-plugin-dist-zip] Found ${filesToCompress.length} files to compress.`));
  
  // 创建输出流
  const output = fs.createWriteStream(outputPath);

  console.log(chalk.yellow(`[vite-plugin-dist-zip] Compressing "${sourceDir}" to "${outputPath}"...`));
  
  // 创建 archiver 实例
  const archive = archiver('zip');

  // 将压缩数据管道传输到输出文件
  archive.pipe(output);

  // 添加文件到压缩包
  for (const file of filesToCompress) {
    const filePath = path.join(sourceDir, file);
    archive.file(filePath, { name: file });
  }

  // 返回一个 Promise，在写入完成后 resolve
  return new Promise((resolve, reject) => {
    // 监听写入完成事件
    output.on('close', () => {
      console.log(chalk.green(`[vite-plugin-dist-zip] Successfully compressed "${sourceDir}" to "${outputPath}" (${archive.pointer()} bytes)`));
      resolve(outputPath);
    });
    
    // 监听压缩错误
    archive.on('error', (err) => {
      console.error(chalk.red('[vite-plugin-dist-zip] Error during compression:'), err);
      reject(err);
    });
    
    // 监听写入错误
    output.on('error', (err) => {
      console.error(chalk.red('[vite-plugin-dist-zip] Error writing file:'), err);
      reject(err);
    });
    
    // 完成压缩
    archive.finalize().catch(reject);
  });
}

/**
 * 上传文件到阿里云 OSS
 * @param {Object} ossConfig - OSS 配置
 * @param {string} filePath - 要上传的文件路径
 * @param {string} fileName - 文件名
 * @returns {Promise<void>}
 */
async function uploadToOSS(ossConfig, filePath, fileName) {
  console.log(chalk.yellow('[vite-plugin-dist-zip] Uploading to OSS...'));
  
  // 创建 OSS 客户端
  const client = new OSS({
    region: ossConfig.region,
    accessKeyId: ossConfig.accessKeyId,
    accessKeySecret: ossConfig.accessKeySecret,
    bucket: ossConfig.bucket,
  });
  
  // 上传文件
  const objectKey = typeof ossConfig.objectKey === 'function' 
    ? ossConfig.objectKey(fileName)
    : (ossConfig.objectKey || fileName);
  const result = await client.put(objectKey, filePath);
  
  console.log(chalk.green(`[vite-plugin-dist-zip] Successfully uploaded to OSS: ${result.url}`));
}

/**
 * Vite 插件 - 将构建后的 dist 目录压缩为 zip 文件
 * @param {Object} options - 插件选项
 * @param {string} [options.fileName='dist.zip'] - 生成的 zip 文件名
 * @param {string} [options.sourceDir='dist'] - 要压缩的源目录
 * @param {string} [options.outputDir='dist'] - zip 文件输出目录
 * @param {Object} [options.oss] - 阿里云 OSS 配置
 * @param {string} [options.oss.region] - OSS 区域
 * @param {string} [options.oss.accessKeyId] - AccessKey ID
 * @param {string} [options.oss.accessKeySecret] - AccessKey Secret
 * @param {string} [options.oss.bucket] - Bucket 名称
 * @param {string|function} [options.oss.objectKey] - 上传到 OSS 的对象键名，可以是字符串或返回字符串的函数
 */
export default function viteDistZip(options = {}) {
  const {
    fileName = 'dist.zip',
    sourceDir = 'dist',
    outputDir = 'dist',
    oss
  } = options;

  return {
    name: 'vite-plugin-dist-zip',

    /**
     * 只在构建时启用该插件
     */
    apply: 'build',

    /**
     * 在 Vite 构建完成后执行压缩操作
     */
    async closeBundle(error) {
      if (error) {
        console.error(chalk.red('[vite-plugin-dist-zip] Error during build:'), error);
        return;
      }
      
      try {
        // 压缩 dist 目录
        const zipFilePath = await compressDist(sourceDir, outputDir, fileName);
        
        // 如果配置了 OSS 上传选项，则上传到阿里云 OSS
        if (oss) {
          await uploadToOSS(oss, zipFilePath, fileName);
        }
      } catch (err) {
        console.error(chalk.red('[vite-plugin-dist-zip] Error:'), err);
        throw err;
      }
    }
  };
}